/**
 * Author Mona Esmaeili  && Gabriele Cerfoglio      
 */

package sa4


import javax.servlet.http._
import scala.xml._
import scala.collection.JavaConversions._
import scala.util.matching.Regex
import javax.servlet.ServletConfig
import scala.io.Source._
import java.io._
import org.squeryl.PrimitiveTypeMode._
import org.squeryl.KeyedEntity
import org.squeryl.annotations.OptionType
import org.squeryl.SessionFactory
import org.squeryl.Session
import org.squeryl.adapters.H2Adapter
import sa4.routing._
import sa4.template._
import java.sql.Timestamp
import java.sql.Date
import scala.collection.mutable.HashMap
import javax.servlet.AsyncContext
import java.util.concurrent.ConcurrentLinkedQueue





class Passenger(val firstName: String, val lastName: String ,val email:String, val destination:String, val departure:String, val code:Int) extends KeyedEntity[Long] {
  val id = 0L
  def this() = this("", "","", "", "", 0)
}

object AirLine extends org.squeryl.Schema {
  val reservations = table[Passenger]
  
  on(reservations)(s =>
    declare(s.code is (unique),
            s.code is (indexed, dbType("Int"))))
}



class SimpleServletWithForms extends HttpServlet {
	
  
 
  
	def fixQuotes(template: NodeSeq): NodeSeq = {
			import scala.xml.transform._
			object FixQuotes extends BasicTransformer {
				override protected def unchanged(n: Node, ns: Seq[Node]) = ns.length == 1 && (ns.head eq n)
				override def transform(n: Node): Seq[Node] = n match {
				case n: Text => new Unparsed(n.text)
				case n => super.transform(n)
				}
			}
			FixQuotes.transform(template)
 }
  
  
  type HandlerFunction = (HttpServletRequest, HttpServletResponse) => Unit
  
  var queue = new ConcurrentLinkedQueue[AsyncContext]
  
  val routing = new Routing()
  val template=new Template() 

  override def init(config: ServletConfig) {
    super.init(config)

    Class.forName("org.h2.Driver")
    SessionFactory.concreteFactory = Some(() =>
      Session.create(
        java.sql.DriverManager.getConnection("jdbc:h2:" + System.getProperty("user.dir") + "/test"),
        new H2Adapter))

    
    transaction {
      
      AirLine.drop
      AirLine.create
      AirLine.reservations.insert(new Passenger ("Someone", "Important", "SI@hotmail.com", "Milan", "Zurich", 1))

    }




    routing.addRoute("/", loadHome)
    routing.addRoute("/Shop.css", css)
	routing.addRoute("/airline.jpg", picture)
	routing.addRoute("/green.jpg", picture1)
    routing.addRoute("/poll", poll)
    routing.addRoute("longPoll", longPoll)
 
   

  }

  override def service(req: HttpServletRequest, resp: HttpServletResponse) {

    if (routing.tableDispatch(req, resp))
      return
    resp.sendError(HttpServletResponse.SC_NOT_FOUND)
  }
  
  	def poll(req: HttpServletRequest, resp: HttpServletResponse) = {
  	  send(resp)
  	}
  	
  	def longPoll(req: HttpServletRequest, resp: HttpServletResponse) = {
  	  var c = req.startAsync()
      c.setTimeout(0)
      queue.offer(c)
  	}
  
    def loadHome(req: HttpServletRequest, resp: HttpServletResponse) = {
    val xml = fixQuotes(template.loadPage("airlineIndex.html"))
    val html = prepareForm(xml)
    resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
  }
    


    def css(req: HttpServletRequest, resp: HttpServletResponse) = {
   val css = template.loadCss("Shop.css")
    resp.getWriter.write(css)
    resp.setContentType("text/css")
  }

    
    def picture(req: HttpServletRequest, resp: HttpServletResponse) {
    val pic = template.loadCss("airline.jpg")
    resp.getWriter.write(pic)
    resp.setContentType("image/jpg")
  }
   
  	def picture1(req: HttpServletRequest, resp: HttpServletResponse) {
    val pic = template.loadCss("green.jpg")
    resp.getWriter.write(pic)
    resp.setContentType("image/jpg")
  }
   
  
  def send(resp: HttpServletResponse) = {
    resp.setContentType("text/html")
    resp.setStatus(HttpServletResponse.SC_OK)
    writeXML(resp, showUsers())
  }
  
  def writeXML(resp: HttpServletResponse, ns: NodeSeq) = resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(ns))
  
  
  def showUsers(): NodeSeq = {
    <p>
		  {
          transaction {
            for (s <- from(AirLine.reservations)(s => where(s.code === s.code) select (s))) yield {
              <div>
                <h3>Information on passenger { s.code }:</h3>
                <ul>
                	<li>First Name: { s.firstName } </li>
                	<li>Last Name: { s.lastName } </li>
                	<li>Email Adress: { s.email } </li>
                	<li>Destination: { s.destination } </li>
                	<li>Departure: { s.departure } </li>
                </ul>
              </div>                     	
            }
          }
        }
	</p>
  }
  
  def update = {
    println("   updating")

    while (!queue.isEmpty) {
      queue.poll match {
        case null =>
        case c =>
          println("   update " + c)
          c.getResponse match {
            case resp: HttpServletResponse => send(resp)
            case _ =>
              throw new MatchError
          }
          c.complete
      }
    }
  }


    def searchPassenger(code: String) = {
      <html>
      <head>
        <title>Results for { code }</title>
      </head>
      <body>
        {
          if (code.forall(_.isDigit) && code.length() != 0) {
          transaction {
            for (s <- from(AirLine.reservations)(s => where(s.code === (code.toInt)) select (s))) yield {
              <div>
                <h3>Information on passenger { s.code }:</h3>
                <ul>
                	<li>First Name: { s.firstName } </li>
                	<li>Last Name: { s.lastName } </li>
                	<li>Email Adress: { s.email } </li>
                	<li>Destination: { s.destination } </li>
                	<li>Departure: { s.departure } </li>
                </ul>
              </div>                     	
            }
          }
          }
          else {
            <h3>Invalid Passenger Code</h3>
          }
        }
        <a href="/">Back</a>
      </body>
    </html>
    }
    
    
    
    def registerPassenger(firstName: String, lastName: String, email: String, destination: String, departure: String, code: String) = {
      
      
   
    val filled = firstName.length() != 0 && lastName.length() != 0 && email.length() != 0 && destination.length() != 0 && departure.length() != 0
    
    if (code.forall(_.isDigit) && code.length() != 0 && filled) {
      
      transaction {
      AirLine.reservations.insert(new Passenger(firstName, lastName, email, destination, departure, code.toInt))
    }

    <html>
      <head>
        <title>User detail</title>
        <link rel="stylesheet" href="reserve.css"/>
      </head>
      <body>
        <h3> Passenger { code }, { firstName } { lastName } has been added </h3>
        <a href="/">Back</a>
        
      </body>
    </html>  
    }
    else {
      <html>
      <head>
        <title>User detail</title>
        <link rel="stylesheet" href="reserve.css"/>
      </head>
      <body>
        <h3> Invalid Passenger Details </h3>
        <a href="/">Back</a>
        
      </body>
    </html>  
    }
    
    
  
    }
    
    
  def removePassenger(code: String) = {
      
     
    transaction {
      AirLine.reservations.deleteWhere(s => s.code === code.toInt)
    }

    <html>
      <head>
        <title>User detail</title>
        <link rel="stylesheet" href="reserve.css"/>
      </head>
      <body>
        <h3> Passenger { code } has been removed </h3>
        <a href="/">Back</a>
        
      </body>
    </html>
  }
    
       
    
    

   
   

  
  
  
 
  def prepareForm(template: NodeSeq): NodeSeq = {
    import scala.xml.transform._
    import scala.xml.Null // hide scala.Null
    object MakeForm extends RewriteRule {
      override def transform(n: Node): Seq[Node] = n match {
        case form: Elem if form.label == "form" =>
          val Some(handler) = form.attribute("handler")
          var Some(method) = form.attribute("method")

          // Make the handler and install in routeTable
          val (uri, f: HandlerFunction) = makeHandler(handler.text)

          routing.addRoute(uri, f)

          // Add an action to the form.
          form % Attribute(None, "action", Text(uri), Null)
        case n => n
      }
    }
    template.map(ns => new RuleTransformer(MakeForm)(ns))
  }

  /**
   * Creates a new path with its corresponding function
   * @param s
   * @return String A new path
   * @return HandlerFunction The corresponding function
   */
  def makeHandler(s: String): (String, HandlerFunction) = {
    // Parse the handler string.
    val q = "^(\\w+)\\((.*)\\)$".r

    val (methodName, argNames) = s match {
      case q(methodName, argNames) => (methodName, argNames.split(",").map(_.trim))
      case _ => throw new RuntimeException("bad handler: " + s)
    }

    // Look for a method that takes the right number of strings.
    val method = this.getClass.getMethod(methodName, argNames.map(_ => classOf[String]): _*)
    val h = (req: HttpServletRequest, resp: HttpServletResponse) => {
      val result = method.invoke(this, argNames.map(k => req.getParameter(k)): _*)
      result match {
        case s: String =>
          resp.getWriter.write(s)
        case html: NodeSeq =>
          resp.getWriter.write(new PrettyPrinter(72, 2).formatNodes(html))
      }
    }
    (handlerURI(methodName), h)
  }

  /**
   * Constructs a new path with the method name
   * @param methodName The method that will be used as path
   */
  def handlerURI(methodName: String) = {
    "/" + methodName
  }
  
}
  




